#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

// 定义接收消息人员的消息类型
#define MSG_DISEN  0x05
#define MSG_JACK   0x06
#define MSG_LUYC   0x07

typedef struct
{
    long type;      // 接收者类型
    char text[100]; // 发送内容
    char name[20];  // 发送者姓名
} MSG;

// ./chat <用户名>
int main(int argc, char const *argv[])
{
    // 0. 依据命令行指定的用户，确认当前程序中消息类型
    if(argc != 2) return -1;
    long my_msg_type = 0; // 自己消息的类型

    // 可选发送人(消息类型)
    char *selectNames[2];

    if(strncmp("disen", argv[1], 5) == 0){
        my_msg_type  = MSG_DISEN;
        selectNames[0] = "jack";
        selectNames[1] = "lucy";
    }else  if(strncmp("lucy", argv[1], 4) == 0){
        my_msg_type  = MSG_LUYC;
        selectNames[0] = "disen";
        selectNames[1] = "jack";
    }else  if(strncmp("jack", argv[1], 4) == 0){
        my_msg_type  = MSG_JACK;
        selectNames[0] = "disen";
        selectNames[1] = "lucy";
    }

    // 1. 创建消息队列
    key_t key = ftok("/", 3);
    int msgid = msgget(key, IPC_CREAT|0644);

    // 2. 创建子进程，并负责接收消息
    MSG msg;
    if(fork() == 0){
       
        while(1){
            // 接收自己的数据
            bzero(&msg, sizeof(msg)); // 清空数据
            msgrcv(msgid, &msg, sizeof(MSG), my_msg_type, 0); // 阻塞

            printf("%s: %s\n", msg.name, msg.text);
        }
    }

    // 3. 主（父）进程， 负责从键盘接收数据，并选择向哪位人员发送消息
    char buf[128] = "";
    while(1){
        printf("@ %s, %s\n", selectNames[0], selectNames[1]);
        // 从键盘接收数据格式： @disen:你好, @lucy:hi, @jack:good,  @0
        bzero(buf, 128);
        bzero(&msg, sizeof(MSG));  // 清空消息
        fgets(buf, 128, stdin);
        buf[strlen(buf)-1] = 0; // 去\n

        if(strncmp("@", buf, 1) != 0){
            printf("输入的格式有问题，请按@用户名:消息的格式输入!\n");
            continue;
        }
        if(strncmp("@0", buf, 2) == 0){
            break;
        }

        // 处理数据
        char *sendName = strtok(buf, ":");  // 读取发送目标人的姓名
        if(strncmp("disen", sendName+1, 5) == 0){
            msg.type = MSG_DISEN;  
        }else  if(strncmp("lucy", sendName+1, 4) == 0){
            msg.type = MSG_LUYC;
        }else  if(strncmp("jack", sendName+1, 4) == 0){
           msg.type = MSG_JACK;
        }
        // 读取输入的消息内容
        char *info = strtok(NULL, ":");
        // 封装发送消息
        strncpy(msg.text, info, strlen(info));
        strncpy(msg.name, argv[1], strlen(argv[1])); 

        msgsnd(msgid, &msg, sizeof(MSG), 0);
    }
    // 向当前进程组中所有进程发送停止信号
    kill(0, SIGSTOP);
    wait(NULL);
    return 0;
}
